<!DOCTYPE html>
<html lang="en-US">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <title>前端性能优化 | Blog</title>
    <meta name="generator" content="VuePress 1.9.10">
    <link rel="icon" href="/blog/img/logo.png">
    <link rel="manifest" href="/blog/manifest.json">
    <link rel="apple-touch-icon" href="/blog/icons/apple-touch-icon-152x152.png">
    <link rel="mask-icon" href="/blog/icons/safari-pinned-tab.svg" color="#3eaf7c">
    <meta name="description" content="blog | js | node | vue | vuepress | webpack">
    <meta name="theme-color" content="#3eaf7c">
    <meta name="apple-mobile-web-app-capable" content="yes">
    <meta name="apple-mobile-web-app-status-bar-style" content="black">
    <meta name="msapplication-TileImage" content="/icons/msapplication-icon-144x144.png">
    <meta name="msapplication-TileColor" content="#000000">
    
    <link rel="preload" href="/blog/assets/css/0.styles.b6b82f87.css" as="style"><link rel="preload" href="/blog/assets/js/app.1f72a262.js" as="script"><link rel="preload" href="/blog/assets/js/2.d3a12b6d.js" as="script"><link rel="preload" href="/blog/assets/js/1.f8bb34da.js" as="script"><link rel="preload" href="/blog/assets/js/28.276df742.js" as="script"><link rel="prefetch" href="/blog/assets/js/10.fde088a2.js"><link rel="prefetch" href="/blog/assets/js/11.c62b6b34.js"><link rel="prefetch" href="/blog/assets/js/12.27d4f152.js"><link rel="prefetch" href="/blog/assets/js/13.419d7748.js"><link rel="prefetch" href="/blog/assets/js/14.eb7a3d07.js"><link rel="prefetch" href="/blog/assets/js/15.114dfd5c.js"><link rel="prefetch" href="/blog/assets/js/16.85253907.js"><link rel="prefetch" href="/blog/assets/js/17.c2838453.js"><link rel="prefetch" href="/blog/assets/js/18.3256f17f.js"><link rel="prefetch" href="/blog/assets/js/19.d8afd0ae.js"><link rel="prefetch" href="/blog/assets/js/20.0d880388.js"><link rel="prefetch" href="/blog/assets/js/21.33b300c9.js"><link rel="prefetch" href="/blog/assets/js/22.2901c590.js"><link rel="prefetch" href="/blog/assets/js/23.b74cc977.js"><link rel="prefetch" href="/blog/assets/js/24.e7339d1a.js"><link rel="prefetch" href="/blog/assets/js/25.ca9a652b.js"><link rel="prefetch" href="/blog/assets/js/26.486b2b13.js"><link rel="prefetch" href="/blog/assets/js/27.1884e308.js"><link rel="prefetch" href="/blog/assets/js/29.4c491e42.js"><link rel="prefetch" href="/blog/assets/js/3.af33e5d6.js"><link rel="prefetch" href="/blog/assets/js/30.20482e57.js"><link rel="prefetch" href="/blog/assets/js/31.75f2ce85.js"><link rel="prefetch" href="/blog/assets/js/32.44f43246.js"><link rel="prefetch" href="/blog/assets/js/33.2996c854.js"><link rel="prefetch" href="/blog/assets/js/34.2ad07be7.js"><link rel="prefetch" href="/blog/assets/js/35.33a7e89c.js"><link rel="prefetch" href="/blog/assets/js/36.b852c680.js"><link rel="prefetch" href="/blog/assets/js/37.899a12b5.js"><link rel="prefetch" href="/blog/assets/js/38.162f5233.js"><link rel="prefetch" href="/blog/assets/js/39.c65fce7b.js"><link rel="prefetch" href="/blog/assets/js/4.0a92875f.js"><link rel="prefetch" href="/blog/assets/js/40.7c1b09b5.js"><link rel="prefetch" href="/blog/assets/js/41.9d3ac8ff.js"><link rel="prefetch" href="/blog/assets/js/42.cd05e187.js"><link rel="prefetch" href="/blog/assets/js/43.397664c2.js"><link rel="prefetch" href="/blog/assets/js/44.0bd439fc.js"><link rel="prefetch" href="/blog/assets/js/45.8230a2d9.js"><link rel="prefetch" href="/blog/assets/js/46.037916dd.js"><link rel="prefetch" href="/blog/assets/js/47.c6fb17e0.js"><link rel="prefetch" href="/blog/assets/js/48.4407ce14.js"><link rel="prefetch" href="/blog/assets/js/49.3881911f.js"><link rel="prefetch" href="/blog/assets/js/5.3b9be48d.js"><link rel="prefetch" href="/blog/assets/js/50.ddcdd5d1.js"><link rel="prefetch" href="/blog/assets/js/51.ae4c3ad2.js"><link rel="prefetch" href="/blog/assets/js/52.0bedd932.js"><link rel="prefetch" href="/blog/assets/js/53.36145b4b.js"><link rel="prefetch" href="/blog/assets/js/54.7cefd728.js"><link rel="prefetch" href="/blog/assets/js/55.d7838999.js"><link rel="prefetch" href="/blog/assets/js/56.fd91fbf0.js"><link rel="prefetch" href="/blog/assets/js/57.5f4eb38a.js"><link rel="prefetch" href="/blog/assets/js/58.db550faf.js"><link rel="prefetch" href="/blog/assets/js/59.e6de6dee.js"><link rel="prefetch" href="/blog/assets/js/6.7b96ebf4.js"><link rel="prefetch" href="/blog/assets/js/60.7d9a1afb.js"><link rel="prefetch" href="/blog/assets/js/61.fff94771.js"><link rel="prefetch" href="/blog/assets/js/62.74513aaa.js"><link rel="prefetch" href="/blog/assets/js/63.111aab67.js"><link rel="prefetch" href="/blog/assets/js/64.b0b6c26f.js"><link rel="prefetch" href="/blog/assets/js/65.65aade87.js"><link rel="prefetch" href="/blog/assets/js/66.a13d5c25.js"><link rel="prefetch" href="/blog/assets/js/7.7e58b9c4.js"><link rel="prefetch" href="/blog/assets/js/vendors~docsearch.2493936b.js">
    <link rel="stylesheet" href="/blog/assets/css/0.styles.b6b82f87.css">
  </head>
  <body>
    <div id="app" data-server-rendered="true"><div class="theme-container"><header class="navbar"><div class="sidebar-button"><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" role="img" viewBox="0 0 448 512" class="icon"><path fill="currentColor" d="M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z"></path></svg></div> <a href="/blog/" class="home-link router-link-active"><!----> <span class="site-name">Blog</span></a> <div class="links"><div class="search-box"><input aria-label="Search" autocomplete="off" spellcheck="false" value=""> <!----></div> <nav class="nav-links can-hide"><div class="nav-item"><a href="/blog/" class="nav-link">
  home
</a></div><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="long-ui" class="dropdown-title"><span class="title">long-ui</span> <span class="arrow down"></span></button> <button type="button" aria-label="long-ui" class="mobile-dropdown-title"><span class="title">long-ui</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="/blog/ui/" class="nav-link">
  UI简介
</a></li><li class="dropdown-item"><!----> <a href="https://weberlong.github.io/long-ui/" target="_blank" rel="noopener noreferrer" class="nav-link external">
  预览
  <span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></li><li class="dropdown-item"><!----> <a href="https://github.com/WeberLong/long-ui" target="_blank" rel="noopener noreferrer" class="nav-link external">
  源码
  <span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></li></ul></div></div><div class="nav-item"><a href="/blog/archives/" class="nav-link router-link-active">
  archives
</a></div><div class="nav-item"><a href="/blog/interview/" class="nav-link">
  interview
</a></div> <a href="https://github.com/WeberLong" target="_blank" rel="noopener noreferrer" class="repo-link">
    GitHub
    <span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></nav></div></header> <div class="sidebar-mask"></div> <aside class="sidebar"><nav class="nav-links"><div class="nav-item"><a href="/blog/" class="nav-link">
  home
</a></div><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="long-ui" class="dropdown-title"><span class="title">long-ui</span> <span class="arrow down"></span></button> <button type="button" aria-label="long-ui" class="mobile-dropdown-title"><span class="title">long-ui</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="/blog/ui/" class="nav-link">
  UI简介
</a></li><li class="dropdown-item"><!----> <a href="https://weberlong.github.io/long-ui/" target="_blank" rel="noopener noreferrer" class="nav-link external">
  预览
  <span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></li><li class="dropdown-item"><!----> <a href="https://github.com/WeberLong/long-ui" target="_blank" rel="noopener noreferrer" class="nav-link external">
  源码
  <span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></li></ul></div></div><div class="nav-item"><a href="/blog/archives/" class="nav-link router-link-active">
  archives
</a></div><div class="nav-item"><a href="/blog/interview/" class="nav-link">
  interview
</a></div> <a href="https://github.com/WeberLong" target="_blank" rel="noopener noreferrer" class="repo-link">
    GitHub
    <span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></nav>  <ul class="sidebar-links"><li><section class="sidebar-group depth-0"><a href="/blog/archives/" class="sidebar-heading clickable router-link-active open"><span>我的文章</span> <!----></a> <ul class="sidebar-links sidebar-group-items"><li><a href="/blog/archives/component.html" class="sidebar-link">Vue组件开发</a></li><li><a href="/blog/archives/webpack.html" class="sidebar-link">Webpack简介</a></li><li><a href="/blog/archives/rtlcss.html" class="sidebar-link">阿拉伯语右排布局</a></li><li><a href="/blog/archives/monitor.html" class="sidebar-link">性能与错误监控方案</a></li><li><a href="/blog/archives/performance.html" aria-current="page" class="active sidebar-link">前端性能优化</a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header"><a href="/blog/archives/performance.html#从浏览器里输入url后发生了什么" class="sidebar-link">从浏览器里输入URL后发生了什么？</a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header"><a href="/blog/archives/performance.html#_1-开启线程" class="sidebar-link">1. 开启线程</a></li><li class="sidebar-sub-header"><a href="/blog/archives/performance.html#_2-dns域名解析" class="sidebar-link">2. DNS域名解析</a></li><li class="sidebar-sub-header"><a href="/blog/archives/performance.html#_3-建立tcp连接" class="sidebar-link">3. 建立TCP连接</a></li><li class="sidebar-sub-header"><a href="/blog/archives/performance.html#_4-发送http请求" class="sidebar-link">4. 发送HTTP请求</a></li><li class="sidebar-sub-header"><a href="/blog/archives/performance.html#_5-返回http请求" class="sidebar-link">5. 返回HTTP请求</a></li><li class="sidebar-sub-header"><a href="/blog/archives/performance.html#_6-关闭tcp连接" class="sidebar-link">6. 关闭TCP连接</a></li><li class="sidebar-sub-header"><a href="/blog/archives/performance.html#_7-浏览器解析html" class="sidebar-link">7. 浏览器解析HTML</a></li><li class="sidebar-sub-header"><a href="/blog/archives/performance.html#_8-浏览器布局渲染" class="sidebar-link">8. 浏览器布局渲染</a></li></ul></li><li class="sidebar-sub-header"><a href="/blog/archives/performance.html#调试工具" class="sidebar-link">调试工具</a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header"><a href="/blog/archives/performance.html#_1-network" class="sidebar-link">1. Network</a></li><li class="sidebar-sub-header"><a href="/blog/archives/performance.html#_2-lighthouse" class="sidebar-link">2. Lighthouse</a></li><li class="sidebar-sub-header"><a href="/blog/archives/performance.html#_3-performance" class="sidebar-link">3. Performance</a></li></ul></li><li class="sidebar-sub-header"><a href="/blog/archives/performance.html#资源打包分析" class="sidebar-link">资源打包分析</a></li><li class="sidebar-sub-header"><a href="/blog/archives/performance.html#性能优化" class="sidebar-link">性能优化</a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header"><a href="/blog/archives/performance.html#_1-服务端渲染" class="sidebar-link">1. 服务端渲染</a></li><li class="sidebar-sub-header"><a href="/blog/archives/performance.html#_2-静态资源使用cdn" class="sidebar-link">2. 静态资源使用CDN</a></li><li class="sidebar-sub-header"><a href="/blog/archives/performance.html#_3-将-css-放在文件头部-javascript-放在底部" class="sidebar-link">3. 将 CSS 放在文件头部，JavaScript 放在底部</a></li><li class="sidebar-sub-header"><a href="/blog/archives/performance.html#_4-缓存" class="sidebar-link">4. 缓存</a></li><li class="sidebar-sub-header"><a href="/blog/archives/performance.html#_5-压缩" class="sidebar-link">5. 压缩</a></li><li class="sidebar-sub-header"><a href="/blog/archives/performance.html#_6-预加载-懒加载" class="sidebar-link">6. 预加载&amp;懒加载</a></li><li class="sidebar-sub-header"><a href="/blog/archives/performance.html#_7-webpack优化" class="sidebar-link">7. webpack优化</a></li><li class="sidebar-sub-header"><a href="/blog/archives/performance.html#_8-骨架屏" class="sidebar-link">8. 骨架屏</a></li><li class="sidebar-sub-header"><a href="/blog/archives/performance.html#_9-减少重绘和重排" class="sidebar-link">9. 减少重绘和重排</a></li></ul></li></ul></li><li><a href="/blog/archives/recode.html" class="sidebar-link">Docs重构</a></li><li><a href="/blog/archives/cach.html" class="sidebar-link">理解浏览器缓存</a></li><li><a href="/blog/archives/httpcode.html" class="sidebar-link">HTTP 状态码</a></li></ul></section></li></ul> </aside> <main class="page"> <div class="theme-default-content content__default"><h1 id="前端性能优化"><a href="#前端性能优化" class="header-anchor">#</a> 前端性能优化</h1> <p>&quot;春江水暖鸭先知，产品好坏用户知&quot;，产品的好坏决定着用户体验，产品的好坏有很多因素，其中性能是决定因素，怎么优化才能让产品的性能更加优良？我们需要了解浏览器工作过程以及性能调试工具。</p> <h2 id="从浏览器里输入url后发生了什么"><a href="#从浏览器里输入url后发生了什么" class="header-anchor">#</a> 从浏览器里输入URL后发生了什么？</h2> <p>其实这个问题的回答可以非常细致，能从信号与系统、计算机原理、操作系统到网络通信、浏览器内核，再到 DNS 解析、负载均衡、页面渲染等等，但我们主要关注前端方面的内容，将整个过程划分为以下几个阶段。</p> <h3 id="_1-开启线程"><a href="#_1-开启线程" class="header-anchor">#</a> 1. 开启线程</h3> <img src="/blog/img/performance_01.png"> <h4 id="浏览器中的进程"><a href="#浏览器中的进程" class="header-anchor">#</a> <strong>浏览器中的进程</strong></h4> <p><strong>1.</strong>** 浏览器进程****(Browser<strong><strong>进程</strong></strong>)<strong><strong>：浏览器的主进程</strong></strong>(<strong><strong>负责协调，主控</strong></strong>)****，只有一个**</p> <p>1）负责浏览器的界面界面显示，与用户交互，网址栏输入、前进、后退等</p> <p>2）负责管理各个页面，创建和销毁进程</p> <p>3）将页面内容(位图)写入到浏览器内存中，最后将图像显示在屏幕上</p> <p>4）文件存储等功能</p> <p><strong>2.</strong>** 渲染进程****(<strong><strong>浏览器内核， <strong><strong>Renderer</strong></strong> 进程，内部是多线程的</strong></strong>)**：默认一个tab页面一个渲染进程，主要的作用为页面渲染，脚本执行，事件处理等</p> <p><strong>3.GPU</strong>** 进程**：用于3D绘制等，将开启了3D绘制的元素的渲染由CPU转向GPU，也就是开启GPU加速。最多一个</p> <p><strong>4.</strong>** 网络进程**：主要负责页面的网络资源加载，之前是作为一个模块运行在浏览器进程里面，现在独立开来，成为一个单独的进程</p> <p><strong>5.</strong>** 插件进程**：每种类型的插件对应一个进程，仅当使用该插件时才创建</p> <p><strong>6.</strong>** 音频进程**：浏览器音频管理</p> <h4 id="浏览器的线程-浏览器内核包含以下线程"><a href="#浏览器的线程-浏览器内核包含以下线程" class="header-anchor">#</a> <strong>浏览器的线程（浏览器内核包含以下线程）</strong></h4> <p><strong>1.GUI</strong>** 渲染线程**</p> <p>1）负责渲染浏览器界面，解析HTML，CSS，构建DOM树和RenderObject树，布局和绘制</p> <p>2）当界面需要重绘(Repaint)或由于某种操作引发回流(reflow)时，该线程就会执行</p> <p>3）与JS引擎互斥，当执行JS引擎线程时，GUI会pending，当任务队列空闲时，才会继续执行GUI</p> <p><strong>2.JS</strong>** 引擎线程**</p> <p>1）也称为JS内核，负责处理javascript脚本程序</p> <p>2）JS引擎线程负责解析Javascript脚本，运行代码</p> <p>3）JS引擎一直等待任务队列中任务的到来，然后加以处理，浏览器无论什么时候都只有一个JS线程在运行JS程序</p> <p>4）同样注意，GUI渲染线程与JS引擎线程时互斥的，所以如果JS执行的时间过长，这样就会造成页面的渲染不连贯，导致页面渲染加载阻塞。</p> <p><strong>3.</strong>** 事件触发线程**</p> <p>1）事件触发线程归属于浏览器而不是JS引擎(辅助JS引擎)，用来控制事件循环(存在一个事件队列)</p> <p>2）当JS引擎执行代码块如setTimeOut时(也可来自浏览器内核的其他线程，如鼠标点击，Ajax异步请求等)，会将对应的任务添加到事件线程中</p> <p>3）当对应的事件符合触发条件被触发时，该线程会把事件添加到待处理队列的队尾，等待JS引擎的处理</p> <p>4）注意，由于JS的单线程关系，所以这些待处理队列的事件都得排队等待JS引擎的处理(当JS引擎空闲时才会去执行)</p> <p><strong>4.</strong>** 定时触发器线程**</p> <p>1）setInterval、setTimeOut所在线程</p> <p>2）浏览器定时计数器并不是由JavaScript引擎计数的，(因为JavaScript引擎时单线程的，如果处于阻塞线程状态就会影响计时的准确)</p> <p>3）因此通过单独线程来计时并触发(计时完毕后，添加到事件队列中，等待JS引擎空闲后执行)</p> <p>4）注意，W3C在HTML标准中规定要求setTimeOut中低于4ms的时间间隔为4ms</p> <p><strong>5.</strong>** 异步 <strong><strong>HTTP</strong></strong> 请求线程****(IO<strong><strong>线程</strong></strong>)**</p> <p>1）在XMLHttpRequest在连接后是通过浏览器新开一个线程请求</p> <p>2）将检测到状态变更时，如果设置有回调函数，异步线程就产生状态变更事件，将这个回调再放入事件队列中(放入事件触发线程中)。再由JavaScript引擎执行。</p> <p><strong>总结：</strong></p> <ul><li>浏览器是多进程的。</li> <li>js执行的主线程为JS引擎，并且无论何时都只有一个JS线程在运行，所以是单线程执行。</li> <li>GUI渲染线程和JS引擎线程是互斥的，并且JS会阻塞页面的加载和渲染。</li> <li>定时器(setInterval,setTimeout)会在定时器触发器线程中进行计时。</li> <li>定时触发器线程计时结束后需要执行的事件和异步HTTP请求线程的回调事件都会进入到事件触发线程的任务队列中等待JS引擎的执行。</li></ul> <h3 id="_2-dns域名解析"><a href="#_2-dns域名解析" class="header-anchor">#</a> 2. DNS域名解析</h3> <p><strong>浏览器缓存</strong> <strong>-&gt;</strong>** 系统缓存 **** -&gt; hosts -&gt; **** 本地域名服务器 **** -&gt; **** 根域名服务器 **** -&gt; **** 顶级域名和权限域名服务器**</p> <p>最终目的是把目标服务器IP地址返回本地主机</p> <img src="/blog/img/performance_02.png"> <h3 id="_3-建立tcp连接"><a href="#_3-建立tcp连接" class="header-anchor">#</a> 3. 建立TCP连接</h3> <img src="/blog/img/performance_03.png"> <ul><li>第一次握手：客户端生成一个随机数seq，假设其值为t，并将标志位SYN设为1，将这些数据打包发给服务器端后，客户端进入等待服务器端确认的状态</li> <li>第二次握手：服务器端收到客户端发来的SYN=1的数据包后，知道这是在请求建立连接，于是服务器端将SYN与ACK都设置为1，并将请求包中客户端发来的随机数t加上1后赋值给ack，然后生成一个服务器端的随机数seq=k，完成这些操作后，服务器端将这些数据打包再发回给客户端，作为对客户端简历连接请求的确认应答</li> <li>第三次握手：客户端收到服务器端的确认应答后，检查数据包中的ack的字段是否为t + 1，ACK是否等于1，若都正确就将服务器端发来的随机数加1（ack = k + 1），将ACK = 1的数据包再发送给服务器端以确认服务器端的应答，服务器端收到应答包后通过检查ack是否等于k + 1来确认连接是否建立成功</li></ul> <h3 id="_4-发送http请求"><a href="#_4-发送http请求" class="header-anchor">#</a> 4. 发送HTTP请求</h3> <p>建立起安全的加密信道后，浏览器开始发送 HTTP 请求，一个请求报文由请求行、请求头、空行、实体（Get 请求没有）组成。请求头由通用首部、请求首部、实体首部、扩展首部组成。其中，通用首部表示无论是请求报文还是响应报文都可以使用，比如 Date；请求首部表示只有在请求报文中才有意义，分为 Accept 首部、条件请求首部、安全请求首部和代理请求首部这四类；实体首部作用于实体内容，分为内容首部和缓存首部这两类；扩展首部表示用户自定义的首部，通过 X-前缀来添加。另外需要注意的是，HTTP 请求头是不区分大小写的，它基于 ASCII 进行编码，而实体可以基于其它编码方式，由 Content-Type决定。</p> <h3 id="_5-返回http请求"><a href="#_5-返回http请求" class="header-anchor">#</a> 5. 返回HTTP请求</h3> <p>服务器接受并处理完请求，返回 HTTP 响应，一个响应报文格式基本等同于请求报文，由响应行、响应头、空行、实体组成。区别于请求头，响应头有自己的响应首部集，比如 Vary、Set-Cookie，其它的通用首部、实体首部、扩展首部则共用。此外，浏览器和服务器必须保证 HTTP 的传输顺序，各自维护的队列中请求/响应顺序必须一一对应，否则会出现乱序而出错的情况。</p> <h3 id="_6-关闭tcp连接"><a href="#_6-关闭tcp连接" class="header-anchor">#</a> 6. 关闭TCP连接</h3> <img src="/blog/img/performance_04.png"> <ul><li>第一次挥手：由客户端先向服务器端发送FIN = M指令，随后进入完成等待状态FIN_WAIT_!，表明客户端已经没有再向服务器端发送的数据，但若服务器端此时还有未完成的数据传递，可继续传递数据</li> <li>第二次挥手：当服务器端收到客户端的FIN报文后，会先发送ack=M+1的确认，告知客户端关闭请求已收到，但可能由于服务器端还有未完成的数据窜提，所以请客户端继续等待</li> <li>第三次挥手：当服务器端确认已完成所有的数据传递后，便发送带有FIN=N的报文给客户端，准备关闭连接</li> <li>第四次挥手：客户端收到FIN=N的报文后可进行关闭操作，但为保证数据正确性，会回传给服务器端一个确认报文ack=N+1，同时服务器端也在等待客户端的最终确认，如果服务器端没有收到报文则会进行重传，只有收到报文后才会真正断开连接。而客户端在发送了确认报文一段时间后，没有收到服务器端任何信息则会任务服务器端连接已关闭，也可关闭客户端信息。</li></ul> <h3 id="_7-浏览器解析html"><a href="#_7-浏览器解析html" class="header-anchor">#</a> 7. 浏览器解析HTML</h3> <p><strong>1.</strong> <strong>解码（</strong>** encoding **<strong>）</strong></p> <p>传输回来的其实都是一些二进制字节数据，浏览器需要根据文件指定编码（例如UTF-8）转换成字符串，也就是HTML 代码。</p> <p><strong>2.</strong> <strong>预解析（</strong>** pre-parsing **<strong>）</strong></p> <p>预解析做的事情是提前加载资源，减少处理时间，它会识别一些会请求资源的属性，比如 img标签的 src属性，并将这个请求加到请求队列中。</p> <p><strong>3.</strong> <strong>符号化（</strong>** Tokenization **<strong>）</strong></p> <p>符号化是词法分析的过程，将输入解析成符号，HTML 符号包括，开始标签、结束标签、属性名和属性值。它通过一个状态机去识别符号的状态，比如遇到 &lt;， &gt;状态都会产生变化。</p> <p><strong>4.</strong> <strong>构建树（</strong>** tree construction **<strong>）</strong></p> <p>在上一步符号化中，解析器获得这些标记，然后以合适的方法创建 DOM对象并把这些符号插入到 DOM对象中。</p> <h3 id="_8-浏览器布局渲染"><a href="#_8-浏览器布局渲染" class="header-anchor">#</a> 8. 浏览器布局渲染</h3> <img src="/blog/img/performance_05.png"> <ol><li>处理HTML标记并构建DOM树</li> <li>处理CSS标记并构建CSSOM树</li> <li>将DOM与CSSOM合并成一个render tree</li> <li>根据渲染树来布局，以计算每个节点的几何信息</li> <li>将各个节点绘制到屏幕上</li></ol> <p><strong>注意</strong> ：</p> <p><strong>重绘</strong>** (repaint)**:</p> <p>元素视觉表现属性被改变即触发重绘，如改变visibility，color等，不会影响到dom结构</p> <p><strong>重排</strong>** (reflow)**:</p> <p>与repaint区别就是：所有影响dom的元素布局的事件都会触发重排，同时也会触发repaint。这种开销是非常昂贵的，导致性能下降是必然的，页面元素越多效果越明显。</p> <p>reflow常见情况：</p> <ul><li>增删改DOM节点</li> <li>移动DOM的位置或是动画显示(所以尽量用canvas来做动画)</li> <li>修改width、display等CSS样式</li> <li>resize窗口或是滚动的时候</li> <li>修改网页默认字体(不建议)</li> <li>display:none会触发reflow和repaint，而visibility:hidden只会产生repaint</li></ul> <h2 id="调试工具"><a href="#调试工具" class="header-anchor">#</a> 调试工具</h2> <h3 id="_1-network"><a href="#_1-network" class="header-anchor">#</a> 1. Network</h3> <img src="/blog/img/performance_06.png"> <p>上面这张图可以看到资源加载详情，初步评估影响性能的因素。页面底部是当前加载资源的一个概览，主要关注DOMContentLoaded：DOM渲染完成时间，Load：所有资源加载完成时间。</p> <h4 id="waterfall时间线"><a href="#waterfall时间线" class="header-anchor">#</a> Waterfall时间线</h4> <img src="/blog/img/performance_07.png"> <ul><li>Queueing浏览器将资源放入队列时间</li> <li>Stalled因放入队列时间而发生的停滞时间</li> <li>DNS Lookup DNS解析时间</li> <li>Initial connection建立HTTP连接的时间</li> <li>SSL浏览器与服务器建立安全性连接的时间</li> <li>Waiting等待服务端返回数据的时间</li> <li>Content Download浏览器下载资源的时间</li></ul> <h4 id="performance-monitor性能监控"><a href="#performance-monitor性能监控" class="header-anchor">#</a> Performance monitor性能监控</h4> <img src="/blog/img/performance_08.png"> <h3 id="_2-lighthouse"><a href="#_2-lighthouse" class="header-anchor">#</a> 2. Lighthouse</h3> <img src="/blog/img/performance_09.png"> <p>Performance - 性能检测，如网页的加载速度、响时间等（重点）</p> <p>Accessibility - 铺助检测，如网页的可访问性问题，HTML代码标签之类的优化等</p> <p>Best Practices - 实践性检测，如网页安全性，如是否开启HTTPS、网页存在的漏洞等</p> <p>SEO - Search Engine Optimisation搜索引擎优化检测，如网页title是否符合搜索引擎的优化标准等</p> <p>PWA- 检测对Progressive Web App的性能影响</p> <p>首次内容绘制（First Contentful Paint，FCP）。即浏览器首次将任意内容（如文字、图像、canvas 等）绘制到屏幕上的时间点。</p> <p>最大的内容绘制（Largest Contentful Paint，LCP）加载页面中元素到屏幕上的最长时间点。</p> <p>可交互时间（Time to Interactive，TTI。指的是所有的页面内容都已经成功加载，且能够快速地对用户的操作做出反应的时间点。</p> <p>速度指标（Speed Index，SI）。衡量了首屏可见内容绘制在屏幕上的速度。在首次加载页面的过程中尽量展现更多的内容，往往能给用户带来更好的体验，所以速度指标的值约小越好。</p> <p>总阻塞时间（Total Blocking Time，TBT）网页被阻塞与用户交互的时间（用户无法进行输入）</p> <p>累积布局偏移（Cumulative Layout Shift，CLS）是指一个页面的布局在加载时的偏移程度。布局转移的发生是因为浏览器倾向于异步加载页面元素,浏览器在完成加载之前不知道各个元素会占用多少空间,因此布局会发生剧烈变化。</p> <h3 id="_3-performance"><a href="#_3-performance" class="header-anchor">#</a> 3. Performance</h3> <img src="/blog/img/performance_10.png"> <p><strong>关键时间节点数据：</strong></p> <ul><li>DCL、L</li></ul> <p>DCL(DOMContentLoaded) 表示HTML加载完成事件， L(onLoad) 表示页面所有资源加载完成事件</p> <ul><li>FP、FCP</li></ul> <p>FP(First Paint): 页面在导航后首次呈现出不同于导航前内容的时间点。（首个像素开始绘制到屏幕上的时机，例如一个页面的背景色）</p> <p>FCP(First Contentful Paint): 首次绘制任何文本，图像，非空白canvas或SVG的时间点。</p> <ul><li>LCP</li></ul> <p>LCP: 可视区域&quot;内容&quot;最大的可见元素开始出现在页面上的时间点。</p> <h2 id="资源打包分析"><a href="#资源打包分析" class="header-anchor">#</a> 资源打包分析</h2> <img src="/blog/img/performance_11.png"> <p>打包分析配置</p> <ul><li>nuxtjs</li></ul> <div class="language-js extra-class"><pre class="language-js"><code><span class="token comment">// nuxt.config.js</span>
module<span class="token punctuation">.</span>exports <span class="token operator">=</span> <span class="token punctuation">{</span>
  <span class="token literal-property property">build</span><span class="token operator">:</span> <span class="token punctuation">{</span>
    <span class="token literal-property property">analyze</span><span class="token operator">:</span> <span class="token boolean">true</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>

<span class="token comment">// package.json</span>
<span class="token string-property property">&quot;analyze&quot;</span><span class="token operator">:</span> <span class="token string">&quot;nuxt build --analyze&quot;</span>
</code></pre></div><ul><li>webpack</li></ul> <div class="language-js extra-class"><pre class="language-js"><code>npm install <span class="token operator">--</span>save<span class="token operator">-</span>dev webpack<span class="token operator">-</span>bundle<span class="token operator">-</span>analyzer
<span class="token comment">// webpack.config.js 文件</span>
<span class="token keyword">const</span> BundleAnalyzerPlugin <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'webpack-bundle-analyzer'</span><span class="token punctuation">)</span><span class="token punctuation">.</span>BundleAnalyzerPlugin
module<span class="token punctuation">.</span>exports<span class="token operator">=</span><span class="token punctuation">{</span>
  <span class="token literal-property property">plugins</span><span class="token operator">:</span> <span class="token punctuation">[</span>
    <span class="token keyword">new</span> <span class="token class-name">BundleAnalyzerPlugin</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
          <span class="token literal-property property">analyzerMode</span><span class="token operator">:</span> <span class="token string">'server'</span><span class="token punctuation">,</span>
          <span class="token literal-property property">analyzerHost</span><span class="token operator">:</span> <span class="token string">'127.0.0.1'</span><span class="token punctuation">,</span>
          <span class="token literal-property property">analyzerPort</span><span class="token operator">:</span> <span class="token number">8889</span><span class="token punctuation">,</span>
          <span class="token literal-property property">reportFilename</span><span class="token operator">:</span> <span class="token string">'report.html'</span><span class="token punctuation">,</span>
          <span class="token literal-property property">defaultSizes</span><span class="token operator">:</span> <span class="token string">'parsed'</span><span class="token punctuation">,</span>
          <span class="token literal-property property">openAnalyzer</span><span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
          <span class="token literal-property property">generateStatsFile</span><span class="token operator">:</span> <span class="token boolean">false</span><span class="token punctuation">,</span>
          <span class="token literal-property property">statsFilename</span><span class="token operator">:</span> <span class="token string">'stats.json'</span><span class="token punctuation">,</span>
          <span class="token literal-property property">statsOptions</span><span class="token operator">:</span> <span class="token keyword">null</span><span class="token punctuation">,</span>
          <span class="token literal-property property">logLevel</span><span class="token operator">:</span> <span class="token string">'info'</span>
        <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
  <span class="token punctuation">]</span>
<span class="token punctuation">}</span>

<span class="token comment">// package.json</span>
<span class="token string-property property">&quot;analyz&quot;</span><span class="token operator">:</span> <span class="token string">&quot;NODE_ENV=production npm_config_report=true npm run build&quot;</span>
</code></pre></div><p>也可以通过打包产物分析</p> <img src="/blog/img/performance_12.png"> <h2 id="性能优化"><a href="#性能优化" class="header-anchor">#</a> 性能优化</h2> <h3 id="_1-服务端渲染"><a href="#_1-服务端渲染" class="header-anchor">#</a> 1. 服务端渲染</h3> <p><strong>客户端渲染</strong> ：获取 HTML 文件，根据需要下载 JavaScript 文件，运行文件，生成 DOM，再渲染。</p> <p><strong>服务端渲染</strong> ：服务端返回 HTML 文件，客户端只需解析 HTML。</p> <ul><li>优点：首屏渲染快，SEO 好。</li> <li>缺点：配置麻烦，增加了服务器的计算压力。</li></ul> <p><strong>静态化站点</strong> ：配置 Nuxt.js 应用生成静态站点</p> <ul><li>优化支持。</li> <li>服务端渲染。</li> <li>快速的开发和运行时。</li> <li>定义良好的项目结构。</li> <li>支持无服务器静态站点生成。</li> <li>自动代码拆分。</li> <li>但动态路由配置麻烦。</li></ul> <h3 id="_2-静态资源使用cdn"><a href="#_2-静态资源使用cdn" class="header-anchor">#</a> 2. 静态资源使用CDN</h3> <p>内容分发网络（CDN）是一组分布在多个不同地理位置的 Web 服务器。我们都知道，当服务器离用户越远时，延迟越高。CDN 就是为了解决这一问题，在多个位置部署服务器，让用户离服务器更近，从而缩短请求时间。</p> <h3 id="_3-将-css-放在文件头部-javascript-放在底部"><a href="#_3-将-css-放在文件头部-javascript-放在底部" class="header-anchor">#</a> 3. 将 CSS 放在文件头部，JavaScript 放在底部</h3> <p>如果这些 CSS、JS 标签放在 HEAD 标签里，JS 加载和执行会阻塞 HTML 解析，阻止 CSSOM 构建，并且需要加载和解析很久的话，那么页面就空白了。所以 JS 文件要放在底部（不阻止 DOM 解析，但会阻塞渲染），等 HTML 解析完了再加载 JS 文件，尽早向用户呈现页面的内容。</p> <p>那为什么 CSS 文件还要放在头部呢？因为先加载 HTML 再加载 CSS，会让用户第一时间看到的页面是没有样式的，为了避免这种情况发生，就要将 CSS 文件放在头部了。</p> <h3 id="_4-缓存"><a href="#_4-缓存" class="header-anchor">#</a> 4. 缓存</h3> <ul><li>http缓存</li> <li>Service Worker</li></ul> <h3 id="_5-压缩"><a href="#_5-压缩" class="header-anchor">#</a> 5. 压缩</h3> <ul><li>开启gzip/br</li></ul> <img src="/blog/img/performance_13.png"> <ul><li>js、css、html压缩</li> <li>图片压缩</li></ul> <h3 id="_6-预加载-懒加载"><a href="#_6-预加载-懒加载" class="header-anchor">#</a> 6. 预加载&amp;懒加载</h3> <ul><li>配置reload和prefetch</li></ul> <div class="language-html extra-class"><pre class="language-html"><code><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>link</span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>preload<span class="token punctuation">&quot;</span></span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>xxx<span class="token punctuation">&quot;</span></span><span class="token punctuation">/&gt;</span></span> 
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>link</span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>prefetch<span class="token punctuation">&quot;</span></span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>xxx<span class="token punctuation">&quot;</span></span> <span class="token punctuation">/&gt;</span></span> 
</code></pre></div><ul><li>图片懒加载</li> <li>路由、组件和文件懒加载</li></ul> <div class="language-js extra-class"><pre class="language-js"><code><span class="token function-variable function">Selector</span><span class="token operator">:</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token keyword">import</span><span class="token punctuation">(</span><span class="token comment">/* webpackChunkName: 'Selector' */</span><span class="token string">'@uikit/xx/selector'</span><span class="token punctuation">)</span> 
</code></pre></div><ul><li>多语言懒加载</li></ul> <p>nuxtjs配置：</p> <div class="language-js extra-class"><pre class="language-js"><code>module<span class="token punctuation">.</span>exports <span class="token operator">=</span> <span class="token punctuation">{</span>
  <span class="token literal-property property">strategy</span><span class="token operator">:</span> <span class="token string">'no_prefix'</span><span class="token punctuation">,</span>
  <span class="token literal-property property">locales</span><span class="token operator">:</span> <span class="token punctuation">[</span>
    <span class="token punctuation">{</span> <span class="token literal-property property">code</span><span class="token operator">:</span> <span class="token string">'en-US'</span><span class="token punctuation">,</span> <span class="token literal-property property">name</span><span class="token operator">:</span> <span class="token string">'English'</span><span class="token punctuation">,</span> <span class="token literal-property property">des</span><span class="token operator">:</span> <span class="token string">'英语'</span><span class="token punctuation">,</span> <span class="token literal-property property">iso</span><span class="token operator">:</span> <span class="token string">'en-US'</span><span class="token punctuation">,</span> <span class="token literal-property property">file</span><span class="token operator">:</span> <span class="token string">'en-US.json'</span> <span class="token punctuation">}</span><span class="token punctuation">,</span>
    <span class="token punctuation">{</span> <span class="token literal-property property">code</span><span class="token operator">:</span> <span class="token string">'zh-CN'</span><span class="token punctuation">,</span> <span class="token literal-property property">name</span><span class="token operator">:</span> <span class="token string">'Chinese'</span><span class="token punctuation">,</span> <span class="token literal-property property">des</span><span class="token operator">:</span> <span class="token string">'中文'</span><span class="token punctuation">,</span> <span class="token literal-property property">iso</span><span class="token operator">:</span> <span class="token string">'zh-CN'</span><span class="token punctuation">,</span> <span class="token literal-property property">file</span><span class="token operator">:</span> <span class="token string">'zh-CN.json'</span> <span class="token punctuation">}</span><span class="token punctuation">,</span>
    <span class="token punctuation">{</span> <span class="token literal-property property">code</span><span class="token operator">:</span> <span class="token string">'ja'</span><span class="token punctuation">,</span> <span class="token literal-property property">name</span><span class="token operator">:</span> <span class="token string">'Japanese'</span><span class="token punctuation">,</span> <span class="token literal-property property">des</span><span class="token operator">:</span> <span class="token string">'日语'</span><span class="token punctuation">,</span> <span class="token literal-property property">iso</span><span class="token operator">:</span> <span class="token string">'ja'</span><span class="token punctuation">,</span> <span class="token literal-property property">file</span><span class="token operator">:</span> <span class="token string">'ja.json'</span> <span class="token punctuation">}</span><span class="token punctuation">,</span>
    <span class="token punctuation">{</span> <span class="token literal-property property">code</span><span class="token operator">:</span> <span class="token string">'th'</span><span class="token punctuation">,</span> <span class="token literal-property property">name</span><span class="token operator">:</span> <span class="token string">'Thai'</span><span class="token punctuation">,</span> <span class="token literal-property property">des</span><span class="token operator">:</span> <span class="token string">'泰语'</span><span class="token punctuation">,</span> <span class="token literal-property property">iso</span><span class="token operator">:</span> <span class="token string">'th'</span><span class="token punctuation">,</span> <span class="token literal-property property">file</span><span class="token operator">:</span> <span class="token string">'th.json'</span> <span class="token punctuation">}</span><span class="token punctuation">,</span>
    <span class="token punctuation">{</span> <span class="token literal-property property">code</span><span class="token operator">:</span> <span class="token string">'ar-SA'</span><span class="token punctuation">,</span> <span class="token literal-property property">name</span><span class="token operator">:</span> <span class="token string">'Arabic'</span><span class="token punctuation">,</span> <span class="token literal-property property">des</span><span class="token operator">:</span> <span class="token string">'阿拉伯语'</span><span class="token punctuation">,</span> <span class="token literal-property property">iso</span><span class="token operator">:</span> <span class="token string">'ar-SA'</span><span class="token punctuation">,</span> <span class="token literal-property property">file</span><span class="token operator">:</span> <span class="token string">'ar-SA.json'</span> <span class="token punctuation">}</span><span class="token punctuation">,</span>
  <span class="token punctuation">]</span><span class="token punctuation">,</span>
  <span class="token literal-property property">defaultLocale</span><span class="token operator">:</span> <span class="token string">'en-US'</span><span class="token punctuation">,</span>
  <span class="token literal-property property">vueI18nLoader</span><span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
  <span class="token literal-property property">lazy</span><span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
  <span class="token literal-property property">langDir</span><span class="token operator">:</span> <span class="token string">'i18n/langs/'</span><span class="token punctuation">,</span>
  <span class="token literal-property property">vueI18n</span><span class="token operator">:</span> <span class="token punctuation">{</span>
    <span class="token literal-property property">fallbackLocale</span><span class="token operator">:</span> <span class="token string">'en-US'</span><span class="token punctuation">,</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre></div><p>vue-i18n配置：</p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token keyword">export</span> <span class="token keyword">const</span> i18n <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">VueI18n</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
  <span class="token literal-property property">locale</span><span class="token operator">:</span> <span class="token string">'en'</span><span class="token punctuation">,</span>
  messages<span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token keyword">function</span> <span class="token function">setI18nLanguage</span><span class="token punctuation">(</span><span class="token parameter">lang</span><span class="token punctuation">)</span><span class="token punctuation">{</span>
  i18n<span class="token punctuation">.</span>locale <span class="token operator">=</span> lang
  <span class="token keyword">return</span> lang
<span class="token punctuation">}</span>
<span class="token keyword">export</span> <span class="token keyword">function</span> <span class="token function">loadLanguageAsync</span> <span class="token punctuation">(</span><span class="token parameter">lang</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">return</span> <span class="token keyword">import</span><span class="token punctuation">(</span><span class="token comment">/* webpackChunkName: &quot;lang-[request]&quot; */</span><span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">@/i18n/</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>lang<span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token parameter">langfile</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
    <span class="token keyword">let</span> langFile <span class="token operator">=</span> langfile<span class="token punctuation">.</span>default
    i18n<span class="token punctuation">.</span><span class="token function">setLocaleMessage</span><span class="token punctuation">(</span>lang<span class="token punctuation">,</span> langFile<span class="token punctuation">)</span>
    <span class="token keyword">return</span> <span class="token function">setI18nLanguage</span><span class="token punctuation">(</span>lang<span class="token punctuation">)</span>
  <span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
</code></pre></div><h3 id="_7-webpack优化"><a href="#_7-webpack优化" class="header-anchor">#</a> 7. webpack优化</h3> <p>splitChunks拆包：11M -&gt; 3M -&gt; 108kB</p> <div class="language-js extra-class"><pre class="language-js"><code> <span class="token literal-property property">splitChunks</span><span class="token operator">:</span> <span class="token punctuation">{</span>
  <span class="token literal-property property">minSize</span><span class="token operator">:</span> <span class="token number">51200</span><span class="token punctuation">,</span> <span class="token comment">// 超过50K分包</span>
  <span class="token literal-property property">maxSize</span><span class="token operator">:</span> <span class="token number">819200</span><span class="token punctuation">,</span> <span class="token comment">// 分包最大800K超出再分包</span>
  <span class="token literal-property property">minChunks</span><span class="token operator">:</span> <span class="token number">1</span><span class="token punctuation">,</span> <span class="token comment">// 至少被minChunks个块(chunk)使用的模块，才会被提取</span>
  <span class="token literal-property property">maxAsyncRequests</span><span class="token operator">:</span> <span class="token number">4</span><span class="token punctuation">,</span> <span class="token comment">// 按需加载时的最大并行请求数</span>
  <span class="token literal-property property">maxInitialRequests</span><span class="token operator">:</span> <span class="token number">3</span><span class="token punctuation">,</span> <span class="token comment">// 入口点的最大并行请求数</span>
  <span class="token literal-property property">automaticNameDelimiter</span><span class="token operator">:</span> <span class="token string">'.'</span><span class="token punctuation">,</span> <span class="token comment">// 指定用于生成名称的连接符</span>
  <span class="token literal-property property">chunks</span><span class="token operator">:</span> <span class="token string">'all'</span><span class="token punctuation">,</span>
  <span class="token literal-property property">cacheGroups</span><span class="token operator">:</span> <span class="token punctuation">{</span>
    <span class="token literal-property property">vendors</span><span class="token operator">:</span> <span class="token punctuation">{</span>
      <span class="token literal-property property">name</span><span class="token operator">:</span> <span class="token string">'vendors'</span><span class="token punctuation">,</span>
      <span class="token literal-property property">test</span><span class="token operator">:</span> <span class="token regex"><span class="token regex-delimiter">/</span><span class="token regex-source language-regex">[\\/]node_modules[\\/]</span><span class="token regex-delimiter">/</span></span><span class="token punctuation">,</span><span class="token comment">// 用于控制哪些模块被这个缓存组匹配到。原封不动传递出去的话，它默认会选择所有的模块。可以传递的值类型：RegExp、String和Function;</span>
      <span class="token literal-property property">reuseExistingChunk</span><span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span> <span class="token comment">// 表示是否使用已有的 chunk，如果为 true 则表示如果当前的 chunk 包含的模块已经被抽取出去了，那么将不会重新生成新的</span>
      <span class="token literal-property property">enforce</span><span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
      <span class="token literal-property property">chunks</span><span class="token operator">:</span> <span class="token string">'all'</span><span class="token punctuation">,</span> <span class="token comment">// initial、async和all</span>
      <span class="token literal-property property">priority</span><span class="token operator">:</span> <span class="token operator">-</span><span class="token number">10</span><span class="token punctuation">,</span> <span class="token comment">// 表示抽取权重，数字越大表示优先级越高</span>
    <span class="token punctuation">}</span><span class="token punctuation">,</span>
    <span class="token literal-property property">common</span><span class="token operator">:</span> <span class="token punctuation">{</span>
      <span class="token literal-property property">name</span><span class="token operator">:</span> <span class="token string">'common'</span><span class="token punctuation">,</span>
      <span class="token literal-property property">chunks</span><span class="token operator">:</span> <span class="token string">'all'</span><span class="token punctuation">,</span>
      <span class="token literal-property property">reuseExistingChunk</span><span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
      <span class="token literal-property property">enforce</span><span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
      <span class="token literal-property property">priority</span><span class="token operator">:</span> <span class="token operator">-</span><span class="token number">10</span><span class="token punctuation">,</span>
    <span class="token punctuation">}</span><span class="token punctuation">,</span>
    <span class="token literal-property property">kso</span><span class="token operator">:</span> <span class="token punctuation">{</span>
      <span class="token literal-property property">name</span><span class="token operator">:</span> <span class="token string">'kso'</span><span class="token punctuation">,</span>
      <span class="token literal-property property">test</span><span class="token operator">:</span> <span class="token regex"><span class="token regex-delimiter">/</span><span class="token regex-source language-regex">[\\/]node_modules[\\/](@oem)[\\/]</span><span class="token regex-delimiter">/</span></span><span class="token punctuation">,</span>
      <span class="token literal-property property">reuseExistingChunk</span><span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
      <span class="token literal-property property">enforce</span><span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
      <span class="token literal-property property">chunks</span><span class="token operator">:</span> <span class="token string">'all'</span><span class="token punctuation">,</span>
      <span class="token literal-property property">priority</span><span class="token operator">:</span> <span class="token number">10</span><span class="token punctuation">,</span>
    <span class="token punctuation">}</span><span class="token punctuation">,</span>
    <span class="token literal-property property">ksoUi</span><span class="token operator">:</span> <span class="token punctuation">{</span>
      <span class="token literal-property property">name</span><span class="token operator">:</span> <span class="token string">'ksoUi'</span><span class="token punctuation">,</span>
      <span class="token literal-property property">test</span><span class="token operator">:</span> <span class="token regex"><span class="token regex-delimiter">/</span><span class="token regex-source language-regex">[\\/]node_modules[\\/](@oem)[\\/]ui[\\/]lib[\\/]</span><span class="token regex-delimiter">/</span></span><span class="token punctuation">,</span>
      <span class="token literal-property property">reuseExistingChunk</span><span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
      <span class="token literal-property property">chunks</span><span class="token operator">:</span> <span class="token string">'all'</span><span class="token punctuation">,</span>
      <span class="token literal-property property">enforce</span><span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
      <span class="token literal-property property">priority</span><span class="token operator">:</span> <span class="token number">11</span><span class="token punctuation">,</span>
    <span class="token punctuation">}</span><span class="token punctuation">,</span>
    <span class="token literal-property property">ksoUiLocale</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token comment">// 语言包 /lib/config/locale/</span>
      <span class="token literal-property property">name</span><span class="token operator">:</span> <span class="token string">'ksoUiLocale'</span><span class="token punctuation">,</span>
      <span class="token literal-property property">test</span><span class="token operator">:</span> <span class="token regex"><span class="token regex-delimiter">/</span><span class="token regex-source language-regex">[\\/]node_modules[\\/](@oem)[\\/]ui[\\/]lib[\\/]config[\\/]locale</span><span class="token regex-delimiter">/</span></span><span class="token punctuation">,</span>
      <span class="token literal-property property">chunks</span><span class="token operator">:</span> <span class="token string">'all'</span><span class="token punctuation">,</span>
      <span class="token literal-property property">priority</span><span class="token operator">:</span> <span class="token number">20</span><span class="token punctuation">,</span>
    <span class="token punctuation">}</span><span class="token punctuation">,</span>
    <span class="token literal-property property">uikit</span><span class="token operator">:</span> <span class="token punctuation">{</span>
      <span class="token literal-property property">name</span><span class="token operator">:</span> <span class="token string">'uikit'</span><span class="token punctuation">,</span>
      <span class="token literal-property property">test</span><span class="token operator">:</span> <span class="token regex"><span class="token regex-delimiter">/</span><span class="token regex-source language-regex">[\\/]node_modules[\\/](@uikit)[\\/]</span><span class="token regex-delimiter">/</span></span><span class="token punctuation">,</span>
      <span class="token literal-property property">chunks</span><span class="token operator">:</span> <span class="token string">'all'</span><span class="token punctuation">,</span>
      <span class="token literal-property property">priority</span><span class="token operator">:</span> <span class="token number">20</span><span class="token punctuation">,</span>
    <span class="token punctuation">}</span><span class="token punctuation">,</span>
    <span class="token literal-property property">meta</span><span class="token operator">:</span> <span class="token punctuation">{</span>
      <span class="token literal-property property">name</span><span class="token operator">:</span> <span class="token string">'meta'</span><span class="token punctuation">,</span>
      <span class="token literal-property property">test</span><span class="token operator">:</span> <span class="token regex"><span class="token regex-delimiter">/</span><span class="token regex-source language-regex">[\\/]node_modules[\\/](@meta)[\\/]</span><span class="token regex-delimiter">/</span></span><span class="token punctuation">,</span>
      <span class="token literal-property property">reuseExistingChunk</span><span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
      <span class="token literal-property property">enforce</span><span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
      <span class="token literal-property property">chunks</span><span class="token operator">:</span> <span class="token string">'all'</span><span class="token punctuation">,</span>
      <span class="token literal-property property">priority</span><span class="token operator">:</span> <span class="token number">10</span><span class="token punctuation">,</span>
    <span class="token punctuation">}</span><span class="token punctuation">,</span>
    <span class="token literal-property property">icf</span><span class="token operator">:</span> <span class="token punctuation">{</span>
      <span class="token literal-property property">name</span><span class="token operator">:</span> <span class="token string">'icf'</span><span class="token punctuation">,</span>
      <span class="token literal-property property">test</span><span class="token operator">:</span> <span class="token regex"><span class="token regex-delimiter">/</span><span class="token regex-source language-regex">[\\/]node_modules[\\/](icf.js)[\\/]</span><span class="token regex-delimiter">/</span></span><span class="token punctuation">,</span>
      <span class="token literal-property property">chunks</span><span class="token operator">:</span> <span class="token string">'all'</span><span class="token punctuation">,</span>
      <span class="token literal-property property">priority</span><span class="token operator">:</span> <span class="token number">30</span><span class="token punctuation">,</span>
    <span class="token punctuation">}</span><span class="token punctuation">,</span>
    <span class="token literal-property property">elementui</span><span class="token operator">:</span> <span class="token punctuation">{</span>
      <span class="token literal-property property">name</span><span class="token operator">:</span> <span class="token string">'elementui'</span><span class="token punctuation">,</span>
      <span class="token literal-property property">test</span><span class="token operator">:</span> <span class="token regex"><span class="token regex-delimiter">/</span><span class="token regex-source language-regex">[\\/]node_modules[\\/]element-ui[\\/]</span><span class="token regex-delimiter">/</span></span><span class="token punctuation">,</span>
      <span class="token literal-property property">chunks</span><span class="token operator">:</span> <span class="token string">'all'</span><span class="token punctuation">,</span>
      <span class="token literal-property property">priority</span><span class="token operator">:</span> <span class="token number">20</span><span class="token punctuation">,</span>
    <span class="token punctuation">}</span><span class="token punctuation">,</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre></div><h3 id="_8-骨架屏"><a href="#_8-骨架屏" class="header-anchor">#</a> 8. 骨架屏</h3> <p>用css提前占好位置，当资源加载完成即可填充，减少页面的回流与重绘，同时还能给用户最直接的反馈。</p> <img src="/blog/img/performance_14.png"> <h3 id="_9-减少重绘和重排"><a href="#_9-减少重绘和重排" class="header-anchor">#</a> 9. 减少重绘和重排</h3> <ul><li>用 JavaScript 修改样式时，最好不要直接写样式，而是替换 class 来改变样式。</li> <li>如果要对 DOM 元素执行一系列操作，可以将 DOM 元素脱离文档流，修改完成后，再将它带回文档。推荐使用隐藏元素（display:none）或文档碎片（DocumentFragement），都能很好的实现这个方案。</li></ul> <div class="language-js extra-class"><pre class="language-js"><code><span class="token keyword">const</span> oFragmeng <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">createDocumentFragment</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 先创建文档碎片</span>
<span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">let</span> i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator">&lt;</span> <span class="token number">10000</span><span class="token punctuation">;</span> i<span class="token operator">++</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">let</span> op <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">createElement</span><span class="token punctuation">(</span><span class="token string">'span'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token keyword">let</span> oText <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">createTextNode</span><span class="token punctuation">(</span>i<span class="token punctuation">)</span><span class="token punctuation">;</span>
  op<span class="token punctuation">.</span><span class="token function">appendChild</span><span class="token punctuation">(</span>oText<span class="token punctuation">)</span><span class="token punctuation">;</span>
  oFragmeng<span class="token punctuation">.</span><span class="token function">appendChild</span><span class="token punctuation">(</span>op<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 先附加在文档碎片中</span>
<span class="token punctuation">}</span>
document<span class="token punctuation">.</span>body<span class="token punctuation">.</span><span class="token function">appendChild</span><span class="token punctuation">(</span>oFragmeng<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 最后一次性添加到document中</span>
</code></pre></div></div> <footer class="page-edit"><!----> <!----></footer> <div class="page-nav"><p class="inner"><span class="prev">
      ←
      <a href="/blog/archives/monitor.html" class="prev">
        性能与错误监控方案
      </a></span> <span class="next"><a href="/blog/archives/recode.html">
        Docs重构
      </a>
      →
    </span></p></div> </main></div><div class="global-ui"></div></div>
    <script src="/blog/assets/js/app.1f72a262.js" defer></script><script src="/blog/assets/js/2.d3a12b6d.js" defer></script><script src="/blog/assets/js/1.f8bb34da.js" defer></script><script src="/blog/assets/js/28.276df742.js" defer></script>
  </body>
</html>
